iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
佛心分享-IT 人職涯歷練

從 UI/UX 麻瓜到工程魔法師 | 從看不懂咒語開始系列 第 4

# Day 04|到了 React,為什麼不能直接改畫面?

  • 分享至 

  • xImage
  •  

上一篇我們整理了資料可能存在的不同位置:

UI
↓
Frontend Data
↓
Browser Storage / API
↓
Backend
↓
Database

接下來,如果把前面的備忘錄搬到 React,會發現寫法突然變得很不一樣。

原本使用 JavaScript 時,我們可能會自己建立一個 <li>

const li = document.createElement("li");

li.textContent = text;

memoList.appendChild(li);

但到了 React,卻開始看到:

const [memos, setMemos] = useState<string[]>([]);

以及:

{memos.map(memo => (
  <li>{memo}</li>
))}

我一開始最大的疑問不是:

useState 語法到底怎麼背?

而是:

我明明只是想讓畫面多一筆資料,為什麼 React 不直接讓我建立一個 <li>


原生 JavaScript:直接修改畫面

前面的備忘錄,可以想成這樣:

使用者輸入內容
↓
按下 +
↓
取得 input 的內容
↓
建立 <li>
↓
放進畫面

例如:

addBtn.addEventListener("click", () => {
  const text = memoInput.value.trim();

  if (!text) return;

  const li = document.createElement("li");

  li.textContent = text;

  memoList.appendChild(li);
});

這裡的思考方式比較像:

我現在要怎麼修改 HTML?

所以我們自己操作 DOM:

找到 DOM
↓
建立 DOM
↓
修改 DOM

React:先改資料,再讓畫面跟著變

到了 React,思考方式會稍微不一樣。

我們先準備一份資料:

const [memos, setMemos] = useState<string[]>([]);

這一行如果先不管完整語法,我目前會這樣理解:

memos
→ 現在的備忘錄資料

setMemos
→ 用來更新 memos

一開始:

memos = [];

假設後來加入:

買牛奶

資料就會變成:

["買牛奶"]

而畫面可以根據 memos 顯示內容:

<ul>
  {memos.map((memo, index) => (
    <li key={`${memo}-${index}`}>
      {memo}
    </li>
  ))}
</ul>

所以這次不是我們自己下指令:

幫我建立一個 li

而比較像:

memos 現在有什麼資料?
↓
React 根據這份資料
↓
決定畫面應該長什麼樣子

這也是原生 JavaScript 和 React 很重要的一個思考差異:

原生 JavaScript
我直接修改畫面

React
我先更新資料
↓
React 再根據資料更新畫面

把新增備忘錄的流程串起來

先看一個簡化版:

import { useState } from "react";

export default function Memo() {
  const [text, setText] = useState("");
  const [memos, setMemos] = useState<string[]>([]);

  const handleAdd = () => {
    const value = text.trim();

    if (!value) return;

    setMemos(prev => [...prev, value]);
    setText("");
  };

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />

      <button onClick={handleAdd}>
        +
      </button>

      <ul>
        {memos.map((memo, index) => (
          <li key={`${memo}-${index}`}>
            {memo}
          </li>
        ))}
      </ul>
    </>
  );
}

第一次看到這段,很容易又回到以前的習慣:

第一行背什麼?
↓
第二行背什麼?
↓
第三行為什麼又長這樣?

但如果用前幾篇的方法,就不要先背程式碼。

先追流程。


使用者輸入:

買牛奶

會先發生:

使用者輸入
↓
onChange
↓
setText()
↓
text 更新

接著按下:

流程變成:

onClick
↓
handleAdd()
↓
取得 text
↓
trim()
↓
setMemos()
↓
memos 更新
↓
React Render
↓
畫面出現「買牛奶」

整段程式碼就開始有一條資料流可以追。


onClick 才是這裡負責點擊事件的角色

以前使用 JavaScript 時,我們可能寫:

addBtn.addEventListener("click", handleAdd);

到了 React,常見的寫法是:

<button onClick={handleAdd}>
  +
</button>

所以目前可以先這樣對照:

JavaScript

addEventListener("click", handleAdd)

            ↓

React

onClick={handleAdd}

同樣地:

<input
  onChange={e => setText(e.target.value)}
/>

代表輸入內容改變時,執行對應的 function。

所以:

使用者點擊
→ onClick

輸入內容改變
→ onChange

這和 useEffect 是不同的事情。

useEffect 之後會另外處理,先不要全部混在一起。


setMemos() 到底做了什麼?

這一行:

setMemos(prev => [...prev, value]);

第一次看到也很容易卡住。

例如原本:

prev = ["買牛奶"];

這次新增:

value = "寫文章";

這裡:

[...prev, value]

會得到:

["買牛奶", "寫文章"]

所以:

setMemos(prev => [...prev, value]);

可以先理解成:

拿到原本的備忘錄,再把新的資料加進去,最後用新的 Array 更新 memos

流程就是:

舊資料

["買牛奶"]

↓ 加入新的 value

["買牛奶", "寫文章"]

↓ setMemos()

更新 State

map() 又在做什麼?

現在 memos 可能長這樣:

["買牛奶", "寫文章"]

但畫面需要的是:

<li>買牛奶</li>
<li>寫文章</li>

所以 React 裡常看到:

memos.map((memo, index) => (
  <li key={`${memo}-${index}`}>
    {memo}
  </li>
))

可以把它想成:

["買牛奶", "寫文章"]
↓
一筆一筆處理

買牛奶
↓
<li>買牛奶</li>

寫文章
↓
<li>寫文章</li>

所以我後來比較不會只把 map() 背成:

一個迴圈。

在這個例子裡,更接近:

把 Array 裡的資料轉換成一份可以 Render 的內容。


關於 key

這裡為了讓範例維持簡單:

key={`${memo}-${index}`}

只是暫時讓每一筆 Render 的內容有可以區分的 key

實際專案中,如果資料本身有:

id

通常會優先使用穩定且唯一的 ID,例如:

<li key={memo.id}>

而不是依賴 index。

這個之後遇到列表實務問題時,再另外拆會比較清楚。


實際專案裡,本質上還是同一條資料流

備忘錄很簡單,但到了真正的 React 專案,本質上還是同一件事。

例如列表頁:

API
↓
Response
↓
更新 State
↓
State 傳給 Table
↓
React Render
↓
畫面出現資料

所以當畫面上的資料有問題,我現在會開始反過來找:

這個 Table 現在吃哪份資料?
↓
這份資料來自哪個 State?
↓
State 是誰更新的?
↓
資料是 Props 傳進來的?
還是 API Response?

而不是一看到 UI 有問題,就直接從 JSX 開始修改。


今天真正要記的不是 useState 的格式

如果只記:

const [memos, setMemos] = useState([]);

過一段時間還是可能忘記。

但如果先理解:

State
↓
現在畫面依賴的資料

使用者操作
↓
事件
↓
更新 State
↓
React Render
↓
UI 跟著資料改變

再看到:

useState
setMemos
onClick
onChange
map

它們就不再是幾個分開的咒語。

而是同一條資料流程裡,各自負責不同事情的工具。


對我來說,這也是從原生 JavaScript 進到 React 後,一個很重要的思考轉換:

不要一直想著「我要怎麼直接修改畫面」,而是先確認「現在的資料應該是什麼」。

當資料改變後,React 再根據新的資料重新 Render UI。

下一篇就可以接著追另一個我以前也很容易混在一起的問題:

StateProps 到底差在哪?為什麼有些資料自己管理,有些資料卻是別人傳進來的?


上一篇
Day 03|資料到底住在哪?為什麼重新整理後有的消失、有的還在?
系列文
從 UI/UX 麻瓜到工程魔法師 | 從看不懂咒語開始4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言